#include "AngelCodeInterpreter.h"

using namespace std;
using namespace Ogre;

void grab(int);
void grab(asUINT);
void grab(bool);
void grab(float);
void grab(double);
void grab(const string&);
void grab(void);
void PrintString(string &str);
void PrintNumber(int num);
void PrintFloat(float num);
void PrintVector3(Vector3 &v);



void AS_MessageCallback(const asSMessageInfo *msg, void *param)
{
	const char *type = "ERR ";
	if( msg->type == asMSGTYPE_WARNING ) 
		type = "WARN";
	else if( msg->type == asMSGTYPE_INFORMATION ) 
		type = "INFO";

	printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}

float ConvDegreeToFloat(const Ogre::Degree &d)
{
  return d.valueDegrees();
}

static void DegreeDefaultConstructor(float d, Ogre::Degree *self)
{
	new(self) Degree(d);
}

void registerDegree(asIScriptEngine* engine)
{
	int r;

	r = engine->RegisterObjectType("Degree", sizeof(Degree), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CA); assert( r >= 0 );

	r = engine->RegisterObjectBehaviour("Degree", asBEHAVE_CONSTRUCT,  "void f(float)", asFUNCTION(DegreeDefaultConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("Degree", asBEHAVE_VALUE_CAST, "float f() const", asFUNCTION(ConvDegreeToFloat), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	r = engine->RegisterObjectMethod("Degree", "Degree &opAssign(const Degree &in)", asMETHODPR(Degree, operator =, (const Degree&), Degree&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("Degree", "Degree &opAddAssign(const Degree &in)", asMETHODPR(Degree, operator+=, (const Degree&), Degree&), asCALL_THISCALL); assert( r >= 0 );

}

static void RadianDefaultConstructor(float r, Radian *self)
{
	new(self) Radian(r);
}

//static void RadianDegreeConstructor(Degree d, Radian *self)
//{
//	new(self) Radian(d);
//}

float ConvRadianToFloat(const Ogre::Degree &d)
{
  return d.valueDegrees();
}

void registerRadian(asIScriptEngine* engine)
{
	int r;

	r = engine->RegisterObjectType("Radian", sizeof(Radian), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CA); assert( r >= 0 );

	r = engine->RegisterObjectBehaviour("Radian", asBEHAVE_CONSTRUCT,  "void f(float)", asFUNCTION(RadianDefaultConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("Radian", asBEHAVE_VALUE_CAST, "float f() const", asFUNCTION(ConvRadianToFloat), asCALL_CDECL_OBJLAST); assert( r >= 0 );
//	r = engine->RegisterObjectBehaviour("Radian", asBEHAVE_CONSTRUCT,  "void f(Degree)", asFUNCTION(RadianDegreeConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	r = engine->RegisterObjectMethod("Radian", "Radian &opAssign(const Degree &in)", asMETHODPR(Radian, operator =, (const Radian&), Radian&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("Radian", "Radian &opAddAssign(const Degree &in)", asMETHODPR(Radian, operator+=, (const Radian&), Radian&), asCALL_THISCALL); assert( r >= 0 );
}


static void Vector3DefaultConstructor(Ogre::Vector3 *self)
{
	new(self) Vector3();
}

static void Vector3CopyConstructor(const Vector3 &other, Vector3 *self)
{
	new(self) Vector3(other);
}

static void Vector3InitConstructor(float x, float y, float z, Vector3 *self)
{
	new(self) Vector3(x,y,z);
}

void registerVector3(asIScriptEngine* engine)
{
	int r;

	r = engine->RegisterObjectType("Vector3", sizeof(Vector3), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CA); assert( r >= 0 );

//	r = engine->RegisterObjectProperty("Vector3", "float x", offsetof(Vector3, x)); assert( r >= 0 );
//	r = engine->RegisterObjectProperty("Vector3", "float y", offsetof(Vector3, y)); assert( r >= 0 );
//	r = engine->RegisterObjectProperty("Vector3", "float z", offsetof(Vector3, z)); assert( r >= 0 );

	r = engine->RegisterObjectBehaviour("Vector3", asBEHAVE_CONSTRUCT,  "void f()",                     asFUNCTION(Vector3DefaultConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("Vector3", asBEHAVE_CONSTRUCT,  "void f(const Vector3 &in)",    asFUNCTION(Vector3CopyConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("Vector3", asBEHAVE_CONSTRUCT,  "void f(float, float, float)",  asFUNCTION(Vector3InitConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	r = engine->RegisterObjectMethod("Vector3", "Vector3 &opAddAssign(const Vector3 &in)", asMETHODPR(Vector3, operator+=, (const Vector3 &), Vector3&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("Vector3", "Vector3 &opSubAssign(const Vector3 &in)", asMETHODPR(Vector3, operator-=, (const Vector3 &), Vector3&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("Vector3", "Vector3 &opMulAssign(float)", asMETHODPR(Vector3, operator*=, (float), Vector3&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("Vector3", "Vector3 &opDivAssign(float)", asMETHODPR(Vector3, operator/=, (float), Vector3&), asCALL_THISCALL); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Vector3", "bool opEquals(const Vector3 &in) const", asFUNCTIONPR(operator==, (const Vector3&, const Vector3&), bool), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Vector3", "Vector3 opAdd(const Vector3 &in) const", asFUNCTIONPR(operator+, (const Vector3&, const Vector3&), Vector3), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Vector3", "Vector3 opSub(const Vector3 &in) const", asFUNCTIONPR(operator-, (const Vector3&, const Vector3&), Vector3), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Vector3", "Vector3 opMul(float) const", asFUNCTIONPR(operator*, (const Vector3&, float), Vector3), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Vector3", "Vector3 opMul_r(float) const", asFUNCTIONPR(operator*, (float, const Vector3&), Vector3), asCALL_CDECL_OBJLAST); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Vector3", "Vector3 opDiv(float) const", asFUNCTIONPR(operator/, (const Vector3&, float), Vector3), asCALL_CDECL_OBJFIRST); assert( r >= 0 );

}


void registerQuat(asIScriptEngine* engine)
{
	int r;

	r = engine->RegisterObjectType("Quaternion", sizeof(Ogre::Quaternion), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CA); assert( r >= 0 );


//	r = engine->RegisterObjectProperty("Quaternion", "float w", offsetof(Quaternion, w)); assert( r >= 0 );
//	r = engine->RegisterObjectProperty("Quaternion", "float z", offsetof(Quaternion, z)); assert( r >= 0 );
//	r = engine->RegisterObjectProperty("Quaternion", "float y", offsetof(Quaternion, y)); assert( r >= 0 );
//	r = engine->RegisterObjectProperty("Quaternion", "float x", offsetof(Quaternion, x)); assert( r >= 0 );
}

void registerCamera(asIScriptEngine* engine)
{
	int r;

	r = engine->RegisterObjectType("Camera", sizeof(Camera), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CA); assert( r >= 0 );

	r = engine->RegisterObjectMethod("Camera", "Vector3 &getPosition()", asMETHOD(Camera, getPosition), asCALL_THISCALL); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Camera", "string &getName()", asMETHOD(Camera, getName), asCALL_THISCALL); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Camera", "Real &getNearClipDistance()", asMETHOD(Camera, getNearClipDistance), asCALL_THISCALL); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Camera", "Real &getFarClipDistance()", asMETHOD(Camera, getFarClipDistance), asCALL_THISCALL); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Camera", "Radian &getFOVy()", asMETHOD(Camera, getFOVy), asCALL_THISCALL); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Camera", "void &setFOVy(Radian)", asMETHOD(Camera, setFOVy), asCALL_THISCALL); assert( r >= 0 );
}

void registerRoot(asIScriptEngine* engine)
{
	int r;
	
	r = engine->RegisterObjectType("Root", 0, asOBJ_REF | asOBJ_NOHANDLE); assert( r >= 0 );  
//	r = engine->RegisterObjectType("Root", sizeof(Ogre::Root), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CA); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Root", "Root &getSingleton(void)", asMETHOD(Root, getSingleton), asCALL_THISCALL); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("Root", "Root* &getSingletonPtr()", asMETHOD(Root, getSingletonPtr), asCALL_THISCALL); assert( r >= 0 );
}

void registerSceneManager(asIScriptEngine* engine)
{
	int r;

	r = engine->RegisterObjectType("SceneMananger", sizeof(Ogre::SceneManager), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CA); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("SceneManager", "bool &hasCamera(string)", asMETHOD(SceneManager, hasCamera), asCALL_THISCALL); assert( r >= 0 );
//	r = engine->RegisterObjectMethod("SceneManager", "Camera* &getCamera(string)", asMETHOD(SceneManager, getCamera), asCALL_THISCALL); assert( r >= 0 );
}


void registerSceneNode(asIScriptEngine* engine)
{
	int r;

	r = engine->RegisterObjectType("SceneNode", sizeof(SceneNode), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CA); assert( r >= 0 );
	r = engine->RegisterObjectMethod("SceneNode", "Vector3 &getPosition()", asMETHOD(SceneNode, getPosition), asCALL_THISCALL); assert( r >= 0 );

}




// Convert a string to an int
int ConvStringToInt(const std::string &s)
{
  return atoi(s.c_str());
}


AngelCodeInterpreter::AngelCodeInterpreter()
{
	lang = "angelcode";
	mOutput.clear();
	mCurrentStatement.clear();
	
	mEngine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

	int r = mEngine->SetMessageCallback(asFUNCTION(AS_MessageCallback), 0, asCALL_CDECL); assert( r >= 0 );

	RegisterScriptString(mEngine);
	
	/// Ogre
	registerDegree(mEngine);
	registerRadian(mEngine);
	registerVector3(mEngine);
	registerQuat(mEngine);
	registerRoot(mEngine);
	registerSceneManager(mEngine);
	registerCamera(mEngine);
	registerSceneNode(mEngine);

   
   
	r = mEngine->RegisterObjectBehaviour("string", asBEHAVE_VALUE_CAST, "int f() const", asFUNCTION(ConvStringToInt), asCALL_CDECL_OBJLAST); assert( r >= 0 );


	r = mEngine->RegisterGlobalFunction("void _grab(bool)", asFUNCTIONPR(grab, (bool), void), asCALL_CDECL); assert( r >= 0 );
	r = mEngine->RegisterGlobalFunction("void _grab(int)", asFUNCTIONPR(grab, (int), void), asCALL_CDECL); assert( r >= 0 );
	r = mEngine->RegisterGlobalFunction("void _grab(uint)", asFUNCTIONPR(grab, (asUINT), void), asCALL_CDECL); assert( r >= 0 );
	r = mEngine->RegisterGlobalFunction("void _grab(float)", asFUNCTIONPR(grab, (float), void), asCALL_CDECL); assert( r >= 0 );
	r = mEngine->RegisterGlobalFunction("void _grab(double)", asFUNCTIONPR(grab, (double), void), asCALL_CDECL); assert( r >= 0 );
	r = mEngine->RegisterGlobalFunction("void _grab()", asFUNCTIONPR(grab, (void), void), asCALL_CDECL); assert( r >= 0 );
	r = mEngine->RegisterGlobalFunction("void _grab(const string &in)", asFUNCTIONPR(grab, (const string&), void), asCALL_CDECL); assert( r >= 0 );

	r = mEngine->RegisterGlobalFunction("void Print(string &in)", asFUNCTION(PrintString), asCALL_CDECL); assert( r >= 0 );
	r = mEngine->RegisterGlobalFunction("void Print(int)", asFUNCTION(PrintNumber), asCALL_CDECL); assert( r >= 0 );
	r = mEngine->RegisterGlobalFunction("void Print(float)", asFUNCTION(PrintFloat), asCALL_CDECL); assert( r >= 0 );
//	r = mEngine->RegisterGlobalFunction("void Print(Vector3)", asFUNCTION(PrintVector3), asCALL_CDECL); assert( r >= 0 );

	
	r = mEngine->RegisterObjectType("App", 0, asOBJ_REF | asOBJ_NOHANDLE);
	r = mEngine->RegisterObjectMethod("App", "void quit()", asMETHOD(App, quit), asCALL_THISCALL); assert( r>= 0 );
	r = mEngine->RegisterGlobalProperty("App app", App::getSingletonPtr());
	  
	mEngine->ClearMessageCallback();
	
	std::stringstream msg;
	msg << "AngelScript " << asGetLibraryVersion();
	Console::getSingletonPtr()->print(msg.str());
}

void AngelCodeInterpreter::insertLine(std::string& script)
{
	//script = "_grab(" + script + ")";

	int r = mEngine->ExecuteString(0, script.c_str());
	if( r < 0 )
//		cout << "Invalid script statement. " << endl;
		Console::getSingletonPtr()->print("Invalid script statement. ");
	else if( r == asEXECUTION_EXCEPTION )
//		cout << "A script exception was raised." << endl;
		Console::getSingletonPtr()->print("A script exception was raised.");

	mPrompt = AS_PROMPT;
}

AngelCodeInterpreter::~AngelCodeInterpreter()
{
	// Release the engine
	mEngine->Release();
}

void grab(int v)
{
	cout << v << endl;
}

void grab(asUINT v)
{
	cout << v << endl;
}

void grab(bool v)
{
	cout << boolalpha << v << endl;
}

void grab(float v)
{
	cout << v << endl;
}

void grab(double v)
{
	cout << v << endl;
}

void grab(const string &v)
{
	cout << v << endl;
}

void grab()
{
	// There is no value
}

void PrintString(string &str)
{
   Console::getSingletonPtr()->print(str);
}


void PrintNumber(int num)
{
	std::stringstream out;
	out << num;
   Console::getSingletonPtr()->print(out.str());
}

void PrintFloat(float num)
{
	std::stringstream out;
	out << num;
   Console::getSingletonPtr()->print(out.str());
}

void PrintVector3(Vector3& v)
{
	std::stringstream out;
	out << v;
	Console::getSingletonPtr()->print(out.str());
}
